package contegor.scire.core;

import java.net.ConnectException;
import java.util.List;
import java.util.Map;
import java.util.Set;

import contegor.scire.core.queries.Condition;

/**
 * Interface for all {@link Container} in charge of creating or erasing new concept instances inside them.
 *
 * @version $Revision: 1.3 $
 * @author  Héctor Cirbián Pousa ($Author: hector $)
 */
@Version("$Id: Modifiable.java,v 1.3 2010-02-01 10:55:39 hector Exp $")
public interface Modifiable extends Container {

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Creates inside <code>this</code> a {@link Concept} identified locally by
   * the {@link IdFragment} passed by parameter.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   *
   * @param creationTime long with the new {@link Concept} creation time.
   * @param transactionId {@link TransactionId} of the transaction to which this operation is associated.
   * @param idf {@link IdFragment} that will identify the new {@link Concept}. It must no be in use.
   * @return the created {@link Concept}.
   * @throws DuplicateElementException if already exists an element with the same {@link Id}. 
   */
  public Concept createConcept(long creationTime, TransactionId transactionId, IdFragment idf) throws DuplicateElementException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Creates inside <code>this</code> a {@link Primitive} identified locally by
   * the {@link IdFragment} passed by parameter.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   *
   * @param creationTime long with the new {@link Primitive} creation time.
   * @param transactionId {@link TransactionId} of the transaction to which this operation is associated.
   * @param idf {@link IdFragment} that will identify the new {@link Primitive}. It must no be in use.
   * @param value Object that will be the content of the new {@link Primitive}.
   * @return the created {@link Primitive}.
   * @throws DuplicateElementException if already exists an element with the same {@link Id}.
   */
  public Primitive createPrimitive(long creationTime, TransactionId transactionId, IdFragment idf, Object value) throws DuplicateElementException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Creates inside <code>this</code> an {@link Action} identified locally by
   * the {@link IdFragment} passed by parameter.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   *
   * @param creationTime long with the new {@link Action} creation time.
   * @param transactionId {@link TransactionId} of the transaction to which this operation is associated.
   * @param idf {@link IdFragment} that will identify the new {@link Action}. It must no be in use.
   * @param plugin String that identifies the plug-in with the implementation of the new {@link Action}.
   * @param implementation String that identifies the method inside the plug-in with the implementation of the new {@link Action}.
   * @return the created {@link Action}.
   * @throws DuplicateElementException if already exists an element with the same {@link Id}.
   */
  public Action createAction(long creationTime, TransactionId transactionId, IdFragment idf, String plugin, String implementation) throws DuplicateElementException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Creates inside <code>this</code> an {@link Attribute} identified locally by
   * the {@link IdFragment} passed by parameter.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   *
   * @param creationTime long with the new {@link Action} creation time.
   * @param transactionId {@link TransactionId} of the transaction to which this operation is associated.
   * @param idf {@link IdFragment} that will identify the new {@link Action}. It must no be in use.
   * @param generatorClass String with the name of the class used to create this attribute {@link Condition}.
   * @param generatorMethod String with the name of the method used to create this attribute {@link Condition}.
   * @return the created {@link Attribute}.
   * @throws DuplicateElementException if already exists an element with the same {@link Id}.
   */
  public Attribute createAttribute(long creationTime, TransactionId transactionId, IdFragment idf, String generatorClass, String generatorMethod) throws DuplicateElementException;

  /**
   * <b>Internal use method. Don't call it directly.</b>
   * Incorporates inside <code>this</code> the {@link Container} parameter.
   * This {@link Container} must be empty, must not belong to other {@link Container}
   * and its {@link IdFragment} must not be in use in <code>this</code>.
   *
   * @param container {@link Container} to be incorporated. must be empty, must not belong to other {@link Container}
   *                  and its {@link IdFragment} must not be in use in <code>this</code>.
   * @throws DuplicateElementException if already exists an element in <code>this</code> with the same {@link IdFragment}.
   * @throws WrongStateException if the new {@link Container} is not empty or belons to other {@link Container}.
   */
  public void incorporateContainer(Container container) throws DuplicateElementException, WrongStateException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Creates inside <code>this</code> a {@link Link} between a {@link Connectable} and an {@link Identifiable}.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   * 
   * @param creationTime long with the new {@link Action} creation time.
   * @param transactionId {@link TransactionId} of the transaction to which this operation is associated.
   * @param flow Link.Flow that will have the {@link Link}.
   * @param target {@link Id} of the {@link Link} target. It must be from a {@link Identifiable} and {@link Connectable}.
   * @param sourceStartPoint {@link IdFragment} of the {@link Identifiable} origin of the source of the {@link Link}.
   *        It must belong to <code>this</code> and be a  {@link Connectable}.
   * @param sourceNextPoints if the source is another {@link Link}, this {@link Id} array will contain the
   * 		    target identifiers to which connect the 'parent' {@link Link}s of such source.
   * @return the created {@link Link}.
   * @throws MissingElementException if the source does not exists.
   * @throws DuplicateElementException if the {@link Link} already exists.
   * @throws TransactionException if a collision with other transactions occurs.
   */
  public Link createLink(long creationTime, TransactionId transactionId, Link.Flow flow, Id target, IdFragment sourceStartPoint, Id ... sourceNextPoints) throws MissingElementException, DuplicateElementException, TransactionException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Creates inside <code>this</code> a {@link Link} between a {@link Connectable} and an {@link Identifiable}.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   * 
   * @param creationTime long with the new {@link Action} creation time.
   * @param transactionId {@link TransactionId} of the transaction to which this operation is associated.
   * @param flow Link.Flow that will have the {@link Link}.
   * @param target {@link Id} of the {@link Link} target.
   *        It must be from a {@link Identifiable} and {@link Connectable}.
   * @param source {@link ConnectException} that belongs to this and will be the origin of the new link.
   * @return the created {@link Link}.
   * @throws WrongStateException if the source does not belong to this {@link Modifiable}.
   * @throws DuplicateElementException if the {@link Link} already exists.
   * @throws TransactionException if a collision with other transactions occurs.
   */
  public Link createLink(long creationTime, TransactionId transactionId, Link.Flow flow, Connectable source, Id target) throws WrongStateException, DuplicateElementException, TransactionException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Sets the value of a {@link Primitive} belonging to <code>this</code>.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   */
  public Primitive setPrimitiveValue(long modificationTime, TransactionId transactionId, IdFragment idf, Object value) throws MissingElementException, WrongStateException, TransactionException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Erases an element inside <code>this</code>.
   * 
   * Needs a {@link Modifiable#commit(TransactionId)} to be permanent.
   * 
   * @param transactionId {@link TransactionId} of the transaction to which this operation is associated.
   * @param withLinksErased {@link Set} that will act as place holder for {@link Id}s of {@link Container}s that
   *        have {@link Link}s pointing the deleted element and should be in 'erased' state so this delete can
   *        success. This check will be performed as a consequence {@link Task} of any 'delete' task. If they
   *        fail, the client will receive an exception and will perform a roll-back.
   * @param startPoint {@link IdFragment} of the {@link Identifiable} origin of the element we want to delete.
   *        It must belong to <code>this</code>.
   * @param nextPoints if the element to delete is a {@link Link}, this {@link Id} array will contain the
   *        target identifiers to which connect the 'parent' {@link Link}s of such element.
   * @throws MissingElementException if the element we want to delete does not exists, and NonDeletableElementException 
   *         if is a {@link Container} not empty or an {@link Identifiable} pointed out by other by a {@link Link}.
   * @throws NonDeletableElementException when trying to delete an element that can not be deleted, for example,
   *         because there are {@link Link}s pointing to it.
   * @throws TransactionException if the element is been modified by other transaction.
   */
  public void deleteElement(TransactionId transactionId, Set<Id> withLinksErased, IdFragment startPoint, Id ... nextPoints) throws MissingElementException, NonDeletableElementException, TransactionException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Checks if the transaction is consistent and not corrupted.
   * 
   * @return {@link Map} who's keys are the {@link Id}s of {@link Identifiable} marked as erased
   * for the specified transaction, and who's values are the {@link Id}s of external {@link Container}s
   * that have {@link Link}s to those {@link Identifiable}s. This structure allows to check if that
   * {@link Link}s are also marked as erased by the same transaction or does not exist.
   * @throws TransactionException if the transaction is corrupted. 
   */
  public Map<Id,Set<Id>> checkCommit(TransactionId transactionId) throws TransactionException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Commits all pending changes belonging to a transaction.
   * 
   * @return {@link Id}s {@link List} of elements in other {@link Container} that are no more pointed
   *         by the elements in <code>this</code> that have been finally deleted. It can holds the same
   *         {@link Id} more than once because more than one {@link Link} to the same target may be deleted.
   * @throws TransactionException if the transaction has some kind of inconsistency.
   * @throws MissingElementException if an element involved in the transaction does not exists.
   */
  public List<Id> commit(TransactionId transactionId) throws TransactionException, MissingElementException;

  /**
   * <b>Internal use only method. Don't call it directly.</b>
   * Rejects all pending changes belonging to a transaction.
   * 
   * @return {@link Id}s {@link List} of elements in other {@link Container} that are no more pointed
   *         by the elements in <code>this</code> who's creation has been rejected. It can holds the same
   *         {@link Id} more than once because more than one {@link Link} to the same target may be rejected.
   */
  public List<Id> rollback(TransactionId transactionId) throws TransactionException, MissingElementException;
}
